/**
 * @copyright Copyright (c) 2007-2024, Guangzhou Xaircraft Technology Co., Ltd.
 */
#include <experimental/filesystem>
#include <inttypes.h>
#include <chrono>
#include <stdio.h>  // NOLINT
#include <stdlib.h>  // NOLINT
#include <string.h>  // NOLINT
#include <unistd.h>  // NOLINT
#include <fcntl.h>  // NOLINT
#include <dirent.h>  // NOLINT
#include <pthread.h>  // NOLINT
#include <sys/prctl.h>  // NOLINT
#include <sys/stat.h>  // NOLINT
#include <sys/statfs.h>  // NOLINT
#include <unistd.h>  // NOLINT
#include <sys/timeb.h>  // NOLINT
#include <regex>
#include <experimental/filesystem>    // NOLINT
#include <fstream>
#include <sys/mman.h>  // NOLINT
#include <memory>
#include <algorithm>

#include "ProcessMonitor.h"  // NOLINT
// #include "xaglog/xaglog.hpp"
// #include "linux/linux_util.h"
// #include "json/json_util.h"

#if 0
namespace {
#undef SPDLOG_TRACE
#undef SPDLOG_DEBUG
#undef SPDLOG_INFO
#undef SPDLOG_WARN
#undef SPDLOG_ERROR
#undef SPDLOG_CRITICAL
constexpr char xaglogger[] = "DEAMON";
#define SPDLOG_TRACE(...) XAGLOG_TOPIC_TRACE(xaglogger, ##__VA_ARGS__)
#define SPDLOG_DEBUG(...) XAGLOG_TOPIC_DEBUG(xaglogger, ##__VA_ARGS__)
#define SPDLOG_INFO(...) XAGLOG_TOPIC_INFO(xaglogger, ##__VA_ARGS__)
#define SPDLOG_WARN(...) XAGLOG_TOPIC_WARN(xaglogger, ##__VA_ARGS__)
#define SPDLOG_ERROR(...) XAGLOG_TOPIC_ERROR(xaglogger, ##__VA_ARGS__)
#define SPDLOG_CRITICAL(...) XAGLOG_TOPIC_CRITICAL(xaglogger, ##__VA_ARGS__)
}  // NOLINT
#endif

namespace xag {  // NOLINT
  namespace xos {  // NOLINT
    namespace deamon {  // NOLINT

      namespace fs = std::experimental::filesystem;

      std::vector<const char *> MonitorReport::sheet_title = {"name", "usr",
                                                              "system", "cpu", "threads", "kB_rd/s",
                                                              "kB_wr/s", "cswch/s", "nvcswch/s", "all_ctx",
                                                              "total_mem(MB)", "physical_mem(MB)", "shared_mem(MB)"};

      std::vector<const char *> MonitorReport::sheet_sys_title = {"start_time", "end_time", "cpu",
                                                                "cpu_temp", "user_cpu", "kernel_cpu", "iowait", "hi",
                                                                  "si", "ctxt", "intr", "blocked", "mem", "flash"};
      /**
       * GetCpuTemperature - get cpu temprature.
       *	@none
       */
      float GetCpuTemperature(void) {
/* hi3519dv500芯片内部集成温度传感器(T-Sensor)，温度的检测范围为-40~125℃
 * 单次测量模式
 */
#define MAP_SIZE 4096
#define MAP_MASK (MAP_SIZE - 1)
#define MISC_CTRL0 0x01102A000   // 采集模式寄存器地址
#define V_MISC_CTRL0 0x80000000  // 设置采集模式寄存器地址的值
#define MISC_CTRL2 0x01102A008   // 温度记录码寄存器地址 MISC_CTRL2的bit0~bit9有效

        int dev_fd;
        void *p_MISC_CTRL0 = NULL;
        void *p_MISC_CTRL2 = NULL;
        unsigned int tmp = 0;
        float temprature = 0;  // NOLINT
        long offsetAddr_ctrl0 = MISC_CTRL0 & MAP_MASK;  // NOLINT
        long offsetAddr_ctrl2 = MISC_CTRL2 & MAP_MASK;  // NOLINT

        dev_fd = open("/dev/mem", O_RDWR | O_NDELAY);
        if (dev_fd < 0) {
          printf("open /dev/mem error:%s\n", strerror(errno));
          return 0;
        }
        unsigned int *map_base = (unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
                                                      dev_fd,
                                                      MISC_CTRL0 & ~MAP_MASK);
        if (NULL == map_base) {
          printf("mmap error\n");
          return 0;
        }

        // printf("map_base=0x%08x\n",map_base);
        p_MISC_CTRL0 = map_base + offsetAddr_ctrl0 / 4;
        p_MISC_CTRL2 = map_base + offsetAddr_ctrl2 / 4;
        // printf("p_MISC_CTRL0=0x%08x\n",p_MISC_CTRL0);
        // printf("p_MISC_CTRL2=0x%08x\n",p_MISC_CTRL2);

        *(unsigned int *)p_MISC_CTRL0 = V_MISC_CTRL0;
        *(unsigned int *)p_MISC_CTRL0 = 0x0;
        tmp = *(unsigned int *)p_MISC_CTRL2 & 0x000003ff;
        temprature = 1.0 * (tmp - 132) / 808 * 165 - 40;
        printf("CPU temprature:%0.2f\n", temprature);
        munmap(map_base, MAP_SIZE);

        if (dev_fd)
          close(dev_fd);

        return temprature;
      }

#if 0
float GetCpuTemperature() {
  FILE *file_fd = NULL;
  unsigned int cpu_temp;
  float temprature = 0;
  char buf[1024]{0};

  file_fd = ::fopen("/sys/class/thermal/thermal_zone0/temp", "r");
  if (file_fd == NULL) {
    printf("err:fopen cpu_temp_path error\n");
    return 0;
  }

  ::fgets(buf, sizeof(buf), file_fd);
  ::sscanf(buf, "%u", &cpu_temp);
  ::fclose(file_fd);
  file_fd = NULL;

  temprature = (float)(cpu_temp / 1000.0);  // NOLINT
  printf("CPU temprature:%0.2f\n",temprature);  // NOLINT

  return temprature;
}
#endif

      SysCpuState GetSysCpuState(CpuInfo_t *cpu_info_last) {
        CpuInfo_t cpu_info;
        FILE *file_fd = NULL;
        char buf[1024]{0};

        SysCpuState sys_cpu_state;

        file_fd = ::fopen("/proc/stat", "r");
        if (file_fd == NULL) {
          perror("fopen /proc/stat error");
          return sys_cpu_state;
        }

        xag::xos::deamon::Common::read_uptime(&cpu_info.uptime_cs);

        ::fgets(buf, sizeof(buf), file_fd);
        ::sscanf(buf, "%s%d%d%d%d%d%d%d", cpu_info.name, &cpu_info.user,
                 &cpu_info.nice, &cpu_info.system, &cpu_info.idle, &cpu_info.iowait,
                 &cpu_info.irq, &cpu_info.softirq);

        /* printf(buf, "%s%d%d%d%d%d%d%d", cpu_info.name, cpu_info.user,
                 cpu_info.nice, cpu_info.system, cpu_info.idle, cpu_info.iowait,
                 cpu_info.irq, cpu_info.softirq); */

        while (fgets(buf, sizeof(buf), file_fd) != NULL) {
          if (!strncmp(buf, "ctxt ", 5)) {
            /* Read number of context switches */
            sscanf(buf + 5, "%llu", &cpu_info.ctxt);
          } else if (!strncmp(buf, "intr ", 5)) {
            /* Read number of processes created since system boot */
            sscanf(buf + 5, "%lu", &cpu_info.intr);
          } else if (!strncmp(buf, "procs_blocked ", 14)) {
            /* Read number of processes blocked */
            sscanf(buf + 14, "%llu", &cpu_info.blocked);
          }
        }

        printf("%llu, %lu, %llu\n", cpu_info.ctxt, cpu_info.intr, cpu_info.blocked);

        ::fclose(file_fd);
        file_fd = NULL;

        if (cpu_info_last->user == 0) {
          *cpu_info_last = cpu_info;
          return sys_cpu_state;
        }

        int64_t all_now_cpu_info = cpu_info.user + cpu_info.nice + cpu_info.system +
                                   cpu_info.idle + cpu_info.iowait + cpu_info.irq +
                                   cpu_info.softirq;

        int64_t all_last_cpu_info = cpu_info_last->user + cpu_info_last->nice +
                                    cpu_info_last->system + cpu_info_last->idle +
                                    cpu_info_last->iowait + cpu_info_last->irq +
                                    cpu_info_last->softirq;

        int64_t idle = cpu_info.idle - cpu_info_last->idle;
        int64_t total = all_now_cpu_info - all_last_cpu_info;

        sys_cpu_state.cpu_utilization = (double)(total - idle) * 100 / total;  // NOLINT
        sys_cpu_state.cpu_temperature = GetCpuTemperature();  // NOLINT
        sys_cpu_state.user = (double)(cpu_info.user - cpu_info_last->user) * 100 / total;  // NOLINT
        sys_cpu_state.nice = (double)(cpu_info.nice - cpu_info_last->nice) * 100 / total;  // NOLINT
        sys_cpu_state.system = (double)(cpu_info.system - cpu_info_last->system) * 100 / total;  // NOLINT
        sys_cpu_state.idle = (double)(cpu_info.idle - cpu_info_last->idle) * 100 / total;  // NOLINT
        sys_cpu_state.iowait = (double)(cpu_info.iowait - cpu_info_last->iowait) * 100 / total;  // NOLINT
        sys_cpu_state.irq = (double)(cpu_info.irq - cpu_info_last->irq) * 100 / total;  // NOLINT
        sys_cpu_state.softirq = (double)(cpu_info.softirq - cpu_info_last->softirq) * 100 / total;  // NOLINT

        auto itv = xag::xos::deamon::Common::get_interval(cpu_info_last->uptime_cs, cpu_info.uptime_cs);
        sys_cpu_state.ctxt = ((double)(cpu_info.ctxt - cpu_info_last->ctxt)) / (itv) * 100;  // NOLINT
        sys_cpu_state.intr = ((double)(cpu_info.intr - cpu_info_last->intr)) / (itv) * 100;  // NOLINT
        sys_cpu_state.blocked = cpu_info.blocked;

        *cpu_info_last = cpu_info;

        return sys_cpu_state;
      }

      double GetRamUtilization() {
        FILE *file_fd = NULL;
        Memory_t memory;
        char buf[1024]{0};

        file_fd = ::fopen("/proc/meminfo", "r");
        if (file_fd == NULL) {
          ::perror("fopen /proc/meminfo error");
          return 0;
        }

        ::fgets(buf, sizeof(buf), file_fd);

        ::sscanf(buf, "%s %ld ", memory.name, &memory.total);

        ::fgets(buf, sizeof(buf), file_fd);
        ::sscanf(buf, "%s %ld ", memory.name2, &memory.free);

        ::fgets(buf, sizeof(buf), file_fd);
        ::sscanf(buf, "%s %ld ", memory.name3, &memory.available);

        ::fclose(file_fd);
        file_fd = NULL;

        return (double)(100.0 * (memory.total - memory.available)) / (memory.total);  // NOLINT
      }

      double GetRomUtilization() {
        struct statfs diskInfo;
        if (::statfs("/data", &diskInfo) < 0) {
          ::perror("statfs error");
        }

        // struct statfs {
        //   long f_type;    /* 文件系统类型  */
        //   long f_bsize;   /* 经过优化的传输块大小  */
        //   long f_blocks;  /* 文件系统数据块总数 */
        //   long f_bfree;   /* 可用块数 */
        //   long f_bavail;  /* 非超级用户可获取的块数 */
        //   long f_files;   /* 文件结点总数 */
        //   long f_ffree;   /* 可用文件结点数 */
        //   fsid_t f_fsid;  /* 文件系统标识 */
        //   long f_namelen; /* 文件名的最大长度 */
        // };

        double total = diskInfo.f_blocks - diskInfo.f_bfree + diskInfo.f_bavail;
        if (total != 0.0) {
          // 使用的百分比
          double usage_rate =
              (diskInfo.f_blocks - diskInfo.f_bfree) * 100.0 / total + 1;
          return usage_rate;
        } else {
          return -1.0;
        }
      }

      MonitorReport::MonitorReport() {
        GetSysCpuState(&cpu_info_last);
      }

      MonitorReport::~MonitorReport() {}

      void MonitorReport::init() {
        std::error_code error_code;

        Common::get_HZ();

#if 0
  fs::path switch_path(MONITOR_REPORT_CONFIG_PATH);
  if (!fs::is_regular_file(switch_path)) {
    auto switch_parent_path = switch_path.parent_path();
    if (!fs::is_directory(switch_parent_path)) {
      if (fs::create_directories(switch_parent_path, error_code)) {
        SPDLOG_INFO("Create directories : {}", switch_parent_path.string());
      } else {
        SPDLOG_ERROR("Failed to create a directory : {} , {}",
                     switch_parent_path.string(), error_code.message());
      }
    }

    std::ofstream ofstream(switch_path, std::ios::out);

    if (!ofstream.is_open()) {
      SPDLOG_ERROR("ofstream {} is error!", switch_path.string());
    }

    rapidjson::OStreamWrapper ofstream_osw(ofstream);
    rapidjson::Writer<rapidjson::OStreamWrapper> writer(ofstream_osw);

    writer.StartObject();
    {
      writer.Key("report_switch");
      writer.Bool(report_switch);

      writer.Key("record_duration_min");
      writer.Int(record_duration_min);

      writer.Key("record_max_sheet_cnt");
      writer.Int(record_max_sheet_cnt);
    }
    writer.EndObject();

    ofstream.close();
  } else {
    auto doc = xag::xos::utils::JsonUtil::ReadJson(switch_path.string());
    if (doc.HasParseError() || !doc.IsObject()) {
      SPDLOG_WARN("Read conf is error : {} {}!", switch_path.string(),doc.GetParseError());  // NOLINT
    } else {
      if (doc.HasMember("report_switch") &&
          doc["report_switch"].IsBool()) {
        report_switch = doc["report_switch"].GetBool();
      }

      if (doc.HasMember("record_duration_min") &&
          doc["record_duration_min"].IsInt()) {
        record_duration_min = doc["record_duration_min"].GetInt();
      }

      if (doc.HasMember("record_max_sheet_cnt") &&
          doc["record_max_sheet_cnt"].IsInt()) {
        record_max_sheet_cnt = doc["record_max_sheet_cnt"].GetInt();
      }
    }
  }

  record_interval_time_sec = record_duration_min * 60 / record_max_sheet_cnt;
  SPDLOG_INFO("record_duration_min {} record_max_sheet_cnt {} record_interval_time_sec {}",
            record_duration_min, record_max_sheet_cnt, record_interval_time_sec);

  storage_ = xag_nav::os::Application::Instance()->createStorage();

  if (storage_ == nullptr) {
    SPDLOG_ERROR("Create storage error!");
    report_switch = false;
  }
#endif

        excel_tool[ExcelWorkType::IDLE].excel.NewEmptyExcel();
        excel_tool[ExcelWorkType::IDLE].excel_name = "/data/log/system/" + std::string(PKG_NAME) + "/idle.xls";
        // excel_tool[ExcelWorkType::IDLE].excel_name = "idle.xls";
        // excel_tool[ExcelWorkType::IDLE].excel();

        // excel_tool[ExcelWorkType::WORKING].excel.NewEmptyExcel();
        // excel_tool[ExcelWorkType::WORKING].excel_name = "/data/log/system" + std::string(PKG_NAME) + "/working.xls";

        sync_work_type();
        work_type = get_work_type;
      }

      void MonitorReport::set_pid_stat(const st_pid &pid_stat, uint32_t get_curr, uint64_t now_time) {
        /*if (!report_switch) {
          return ;
        }*/

        uint32_t prev = !curr;
        auto itv = Common::get_interval(uptime_cs[prev], now_time);
        auto ts = itv * Common::SYS_HZ / 100 / 100;

        if (ts >= record_interval_time_sec || uptime_cs[prev] == 0 || get_work_type != work_type) {
          if (!monitor_info.all_process_map[pid_stat.pid].flags) {
            monitor_info.all_process_map[pid_stat.pid].flags = 1;
            for (int i = 0; i < 2; i++) {
              monitor_info.all_process_map[pid_stat.pid].pstats[i] = std::make_unique<pid_stats>();
            }
          }

          if (uptime_cs[prev] == 0) {
            get_curr = !get_curr;
          }

          monitor_info.all_process_map[pid_stat.pid].exist = true;

          Common::get_st_pid(&monitor_info.all_process_map[pid_stat.pid], curr, &pid_stat, get_curr);
        }
      }

      std::string GetLocalTime(void) {
        struct timeb stTimeb;
        struct tm *pTm;
        char psTime[128] = {0};

        ftime(&stTimeb);
        pTm = localtime(&stTimeb.time);  // NOLINT

        /* 系统时间，格式: YYYYMMDDHHMMSS */
        sprintf(psTime, "%04d-%02d-%02d %02d:%02d:%02d.%03d",  // NOLINT
                pTm->tm_year + 1900, pTm->tm_mon + 1, pTm->tm_mday,
                pTm->tm_hour, pTm->tm_min, pTm->tm_sec, stTimeb.millitm);

        return std::string(psTime);
      }

      void MonitorReport::update(uint64_t now_time) {
        /*if (!report_switch) {
          return ;
        }*/

        uint32_t prev = !curr;
        auto itv = Common::get_interval(uptime_cs[prev], now_time);
        auto ts = itv * Common::SYS_HZ / 100 / 100;

        // printf("prev = %d, itv=%ld, ts=%ld\n", prev, itv, ts);

        if (uptime_cs[prev] != 0) {
          // if (ts >= record_interval_time_sec || get_work_type != work_type) {
          if (ts >= record_interval_time_sec) {
            uptime_cs[curr] = now_time;
            monitor_info.sys_stats[curr].ts = GetLocalTime();

            save_excel(prev, curr, itv);
            monitor_info.sys_stats[prev].ts = monitor_info.sys_stats[curr].ts;

            work_type = get_work_type;
            curr ^= 1;
          }
        } else {
          uptime_cs[prev] = now_time;
          monitor_info.sys_stats[prev].ts = GetLocalTime();
          curr ^= 1;
        }
      }

      void MonitorReport::sync_work_type(void) {
        /*if (!report_switch) {
          return;
        }*/

        // auto take_off_state = xag::xos::utils::LinuxUtil::GetTakeOffStat(storage_);
        auto take_off_state = 1;
        if (take_off_state != -1) {
          get_work_type = (take_off_state == 0) ? ExcelWorkType::IDLE : ExcelWorkType::WORKING;
        }

        for (auto &[pid, info] : monitor_info.all_process_map) {
          info.exist = false;
        }
      }

      void MonitorReport::save_excel(int prev,
                                     int curr,
                                     uint64_t itv) {
#if 1
        // SPDLOG_INFO("{}",work_type == ExcelWorkType::IDLE?"idle":"working");
        work_type = ExcelWorkType::IDLE;
        // printf("{work_type=%s}", work_type == ExcelWorkType::IDLE ? "idle" : "working");
        ExcelTool &work_excel = excel_tool[work_type];

        // printf("work_excel.excel.GetTotalWorkSheets()=%d\n", work_excel.excel.GetTotalWorkSheets());
        if (work_excel.excel.GetTotalWorkSheets() >= record_max_sheet_cnt) {
          work_excel.excel.DeleteWorksheet(size_t(0));
        }

        YExcel::BasicExcelWorksheet *sheet = work_excel.excel.AddWorksheet();

        if (sheet) {
          for (int i = 0; i < sheet_sys_title.size(); i++) {
            sheet->Cell(0, i)->Set(sheet_sys_title[i]);
          }

          size_t row = 1;

          auto ram_utilization = GetRamUtilization();
          auto rom_utilization = GetRomUtilization();
          // printf("ram_utilization=%d rom_utilization=%d\n", ram_utilization, rom_utilization);

          auto sys_cpu_state = GetSysCpuState(&cpu_info_last);
          {
            size_t col = 0;
            sheet->Cell(row, col++)->Set(monitor_info.sys_stats[prev].ts.c_str());
            sheet->Cell(row, col++)->Set(monitor_info.sys_stats[curr].ts.c_str());
            sheet->Cell(row, col++)->Set(sys_cpu_state.cpu_utilization);
            sheet->Cell(row, col++)->Set(sys_cpu_state.cpu_temperature);
            sheet->Cell(row, col++)->Set(sys_cpu_state.user);
            sheet->Cell(row, col++)->Set(sys_cpu_state.system);
            sheet->Cell(row, col++)->Set(sys_cpu_state.iowait);
            sheet->Cell(row, col++)->Set(sys_cpu_state.irq);
            sheet->Cell(row, col++)->Set(sys_cpu_state.softirq);
            sheet->Cell(row, col++)->Set(sys_cpu_state.ctxt);
            sheet->Cell(row, col++)->Set(sys_cpu_state.intr);
            sheet->Cell(row, col++)->Set((int)sys_cpu_state.blocked);  // NOLINT
            sheet->Cell(row, col++)->Set((int)ram_utilization);  // NOLINT
            sheet->Cell(row, col++)->Set((int)rom_utilization);  // NOLINT
            ++row;
          }

          row += 2;

          for (int i = 0; i < sheet_title.size(); i++) {
            sheet->Cell(row, i)->Set(sheet_title[i]);
          }

          ++row;

          for (auto it = monitor_info.all_process_map.begin(); it != monitor_info.all_process_map.end();) {
            if (!it->second.exist) {
              // SPDLOG_DEBUG("erase {} pid {}",it->second.name,it->second.pid);
              it = monitor_info.all_process_map.erase(it);
            } else {
              it++;
            }
          }

          for (auto &proess : monitor_info.all_process_map) {
            size_t col = 0;

            auto program_state = Common::get_program_state(&proess.second, prev, curr, itv);
            sheet->Cell(row, col++)->Set(program_state.name.c_str());
            sheet->Cell(row, col++)->Set(program_state.user_cpu);
            sheet->Cell(row, col++)->Set(program_state.system_cpu);
            sheet->Cell(row, col++)->Set(program_state.cpu);
            sheet->Cell(row, col++)->Set((int)program_state.thread_num);  // NOLINT
            sheet->Cell(row, col++)->Set(program_state.rbytes);
            sheet->Cell(row, col++)->Set(program_state.wbytes);
            sheet->Cell(row, col++)->Set(program_state.nvcsw);
            sheet->Cell(row, col++)->Set(program_state.nivcsw);
            sheet->Cell(row, col++)->Set(program_state.nvcsw + program_state.nivcsw);
            sheet->Cell(row, col++)->Set((int)program_state.total_memory);  // NOLINT
            sheet->Cell(row, col++)->Set((int)program_state.physical_memory);  // NOLINT
            sheet->Cell(row, col++)->Set((int)program_state.shared_memory);  // NOLINT

            row++;
          }

          work_excel.excel.SaveAs(work_excel.excel_name.c_str());
          // printf("----------- SaveAs=%s -------------\n", work_excel.excel_name.c_str());
        } else {
          // SPDLOG_ERROR("get sheet error");
          perror("get sheet error");
        }
#endif
      }

      ProcessMonitor::ProcessMonitor() {}

      ProcessMonitor::~ProcessMonitor() {}

      // void ProcessMonitor::init(bool cgroup_switch,const rapidjson::Document& config_document)
      void ProcessMonitor::init(void) {
        Common::read_uptime(&uptime_cs[0]);
        Common::read_uptime(&io_uptime_cs[0]);
        read_stats(0);
        monitor_report.init();
#if 0
  if (cgroup_switch) {
    cgroup_manager_.Init(config_document);
  }
  cpu_affinity_.Init(config_document);
#endif
      }

      st_pid *ProcessMonitor::add_list_pid(pid_t pid, pid_t tgid) {
        if (!pid) {
          // SPDLOG_ERROR("is null");
          perror("is null");
          return nullptr;
        }
        std::unordered_map<pid_t, st_pid>::iterator it;

        st_pid *p = nullptr;

        if (pid == tgid) {
          return nullptr;
        }

        if ((it = all_process_map.find(tgid)) != all_process_map.end()) {
          // pid找不到，tgid找得到表明其为其线程
          // 线程已存储
          if (it->second.sub_thread_map.find(pid) != it->second.sub_thread_map.end()) {
            return &it->second.sub_thread_map[pid];
          }
          // 线程未存储
          p = &it->second.sub_thread_map[pid];
        } else if ((it = all_process_map.find(pid)) != all_process_map.end()) {
          // 找的到的话，表明已经存储了
          return &all_process_map[pid];
        } else {
          // 都找不到表明其为新的父进程
          p = &all_process_map[pid];
        }

        p->tgid = tgid;
        p->pid = pid;

        for (int i = 0; i < 2; i++) {
          p->pstats[i] = std::make_unique<pid_stats>();
          memset(p->pstats[i].get(), 0, PID_STATS_SIZE);
        }

        return p;
      }

      void ProcessMonitor::set_pid_nonexistent(void) {
        for (auto &it : all_process_map) {
          it.second.exist = false;
          for (auto &sub : it.second.sub_thread_map) {
            sub.second.exist = false;
          }
        }
      }

      void ProcessMonitor::sfree_pids(bool force) {
        for (auto it = all_process_map.begin(); it != all_process_map.end();) {
          if (!it->second.exist || force) {
            // SPDLOG_DEBUG("erase {} pid {}",it->second.name,it->second.pid);
            it = all_process_map.erase(it);
          } else {
            // 删除子线程
            for (auto sub = it->second.sub_thread_map.begin(); sub != it->second.sub_thread_map.end();) {
              if (!sub->second.exist || force) {
                // SPDLOG_DEBUG("erase {} pid {}",sub->second.name,sub->second.pid);
                sub = it->second.sub_thread_map.erase(sub);
              } else {
                sub++;
              }
            }

            it++;
          }
        }
      }

      bool ProcessMonitor::read_proc_pid_stat(pid_t pid,
                                              st_pid *plist,
                                              uint32_t *thread_nr,
                                              pid_t tgid,
                                              int curr) {
        int fd, sz, rc, commsz;
        char filename[128];
        static char buffer[1024 + 1];
        char *start, *end;
        struct pid_stats *pst = plist->pstats[curr].get();

        if (tgid) {
          return true;
          sprintf(filename, TASK_STAT, tgid, pid);  // NOLINT
        } else {
          sprintf(filename, PID_STAT, pid);  // NOLINT
        }

        if ((fd = open(filename, O_RDONLY)) < 0)
          /* No such process */
          return false;

        sz = read(fd, buffer, 1024);
        close(fd);
        if (sz <= 0)
          return false;

        buffer[sz] = '\0';

        if ((start = strchr(buffer, '(')) == NULL)
          return false;

        start += 1;
        if ((end = strrchr(start, ')')) == NULL)
          return false;

        commsz = end - start;
        if (commsz >= MAX_NAME_LEN)
          return false;

        char name[MAX_NAME_LEN] = {0};

        memcpy(name, start, commsz);
        name[commsz] = '\0';
        plist->name = name;

        start = end + 2;

        rc = sscanf(start,
                    "%*s %*d %*d %*d %*d %*d %*u %*llu %*llu"
                    " %*llu %*llu %llu %llu %*lld %*lld %*d %*d %u %*u %*d %*llu %*llu"
                    " %*lu %*lu %*lu %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u"
                    " %*u %*u %*u %*u %*llu %llu %*lld\n",
                    &pst->utime, &pst->stime, thread_nr, &pst->gtime);

        if (rc < 3)
          return false;

        if (rc < 4) {
          /* gtime and cgtime fields are unavailable in file */
          pst->gtime = 0;
        }

        return true;
      }

      bool ProcessMonitor::read_proc_pid_sched(pid_t pid, st_pid *plist, pid_t tgid, int curr) {
        int fd, sz, rc = 0;
        char filename[128];
        static char buffer[1024 + 1];
        uint64_t wtime = 0;
        struct pid_stats *pst = plist->pstats[curr].get();

        if (tgid) {
          sprintf(filename, TASK_SCHED, tgid, pid);  // NOLINT
        } else {
          sprintf(filename, PID_SCHED, pid);  // NOLINT
        }

        if ((fd = open(filename, O_RDONLY)) >= 0) {
          /* schedstat file found for process */
          sz = read(fd, buffer, 1024);
          close(fd);
          if (sz > 0) {
            buffer[sz] = '\0';

            rc = sscanf(buffer, "%*u %llu %*d\n", &wtime);
          }
        }

        /* Convert ns to jiffies */
        pst->wtime = wtime * Common::SYS_HZ / 1000000000;

        if (rc < 1)
          return false;

        return true;
      }

      bool ProcessMonitor::read_proc_pid_status(pid_t pid, st_pid *plist, pid_t tgid, int curr) {
        FILE *fp;
        char filename[128], line[256];
        struct pid_stats *pst = plist->pstats[curr].get();

        if (tgid) {
          sprintf(filename, TASK_STATUS, tgid, pid);  // NOLINT
        } else {
          sprintf(filename, PID_STATUS, pid);  // NOLINT
        }

        if ((fp = fopen(filename, "r")) == NULL)
          /* No such process */
          return false;

        while (fgets(line, sizeof(line), fp) != NULL) {
          if (!strncmp(line, "Uid:", 4)) {
            sscanf(line + 5, "%u", &plist->uid);
          } else if (!strncmp(line, "Threads:", 8)) {
            sscanf(line + 9, "%u", &pst->threads);
          } else if (!strncmp(line, "voluntary_ctxt_switches:", 24)) {
            sscanf(line + 25, "%lu", &pst->nvcsw);
          } else if (!strncmp(line, "nonvoluntary_ctxt_switches:", 27)) {
            sscanf(line + 28, "%lu", &pst->nivcsw);
          }
        }

        fclose(fp);

        return true;
      }

      bool ProcessMonitor::read_proc_pid_io(pid_t pid, st_pid *plist, pid_t tgid, int curr) {
        if (tgid || io_read_cnt_ % 5 != 0) {
          return true;
        }   // NOLINT

        FILE *fp;
        char filename[128], line[256];
        struct pid_stats *pst = plist->pstats[curr].get();

        sprintf(filename, PID_IO, pid);  // NOLINT

        if ((fp = fopen(filename, "r")) == NULL) {
          /*
           * Also returns 0 since io stats file doesn't necessarily exist,
           * depending on the kernel version used.
           */
          return true;
        }

        while (fgets(line, sizeof(line), fp) != NULL) {
          if (!strncmp(line, "read_bytes:", 11)) {
            sscanf(line + 12, "%llu", &pst->read_bytes);
          } else if (!strncmp(line, "write_bytes:", 12)) {
            sscanf(line + 13, "%llu", &pst->write_bytes);
          } else if (!strncmp(line, "cancelled_write_bytes:", 22)) {
            sscanf(line + 23, "%llu", &pst->cancelled_write_bytes);
          }
        }
        fclose(fp);

        return true;
      }

      bool ProcessMonitor::read_pid_stats(pid_t pid,
                                          st_pid *plist,
                                          uint32_t *thread_nr,
                                          pid_t tgid, int curr) {
        if (tgid == 0) {
          if (!read_proc_pid_stat(pid, plist, thread_nr, tgid, curr)) {
            return false;
          }

          read_proc_pid_io(pid, plist, tgid, io_curr);
        }

        // read_proc_pid_sched(pid, plist, tgid, curr);

        if (!read_proc_pid_status(pid, plist, tgid, curr)) {
          return false;
        }

        return true;
      }

      void ProcessMonitor::read_task_stats(pid_t pid, st_pid *plist, int curr) {
        pid_t tid;
        char filename[128];
        uint32_t thr_nr;
        st_pid *tlist;

        /* Open /proc/#/task directory */
        sprintf(filename, PROC_TASK, pid);  // NOLINT

        if (!fs::is_directory(filename)) {
          // SPDLOG_WARN("{} is not a directory!", filename);
          printf("{%s} is not a directory!", filename);
          return;
        }

        std::error_code ec;
        for (auto &path : fs::directory_iterator(filename, ec)) {
          if (ec.value() != 0) {
            continue;
          }

          if (!isdigit(path.path().filename().c_str()[0])) {
            continue;
          }

          tid = stoi(path.path().filename());

          tlist = add_list_pid(tid, pid);
          if (!tlist)
            continue;

          tlist->exist = true;

          if (!read_pid_stats(tid, tlist, &thr_nr, pid, curr)) {
            /* Thread doesn't exist */
            tlist->exist = false;
          }
        }
      }

      std::string ReadProgressName(const std::string &pid) {
        std::string process_name_path = "/proc/" + pid + "/comm";
        FILE *file_fd = NULL;
        char buf[1024]{0};
        char name[32] = {0};

        std::string result;

        // printf("name=%s\n", process_name_path.c_str());
        file_fd = ::fopen(process_name_path.c_str(), "r");
        if (file_fd == NULL) {
          ::perror("fopen ProgressName error");
          return 0;
        }

        ::fgets(buf, sizeof(buf), file_fd);
        ::sscanf(buf, "%s", name);

        result = name;
        // 过滤换行符
        if (result.back() == 0x0A) {
          result.pop_back();
        }
        ::fclose(file_fd);

        return result;
      }

      std::string GetProgressPpid(const std::string &pid) {
        std::string file_address = "/proc/" + pid + "/stat";

        std::string str;
        // std::string str(FileUtil::ReadFile(file_address));

        int num = 0;
        std::string data;

        for (auto &c : str) {
          if (c == ' ') {
            ++num;
            if (num == 4) {
              break;
            }

            data.clear();
          } else {
            data.push_back(c);
          }
        }

        return data;
      }

      bool ProcessMonitor::read_stats(int curr) {
        uint32_t thr_nr;
        pid_t pid;
        st_pid *plist;

        set_pid_nonexistent();

        std::error_code ec;
        for (auto &path : fs::directory_iterator(PROC, ec)) {
          if (ec.value() != 0) {
            continue;
          }

          if (!isdigit(path.path().filename().c_str()[0]) ||
              strstr(path.path().filename().c_str(), "usb30drd")) {
            continue;
          }

          pid = stoi(path.path().filename());

          std::string name = ReadProgressName(path.path().filename().c_str());
          if ((name.find(KEYWORDS) == name.npos)) {
            continue;
          }

          if (name == "xag_datapersist") {
            std::string ppid = GetProgressPpid(std::to_string(pid));
            if (all_process_map.find(stoi(ppid)) != all_process_map.end()) {
              continue;
            }
          }

          // cgroup_manager_.CheckCgroupProgress(std::to_string(pid),name);

          // cpu_affinity_.CheckProgress(std::to_string(pid),name);

          plist = add_list_pid(pid, 0);
          if (!plist) {
            // SPDLOG_ERROR("add_list_pid error");
            perror("add_list_pid error");
            continue;
          }

          plist->exist = true;

          if (!read_pid_stats(pid, plist, &thr_nr, 0, curr)) {
            /* PID has terminated */
            plist->exist = false;
          }

          /* Read stats for threads in task subdirectory */
          read_task_stats(pid, plist, curr);
        }

        sfree_pids(false);

        return true;
      }

      int ProcessMonitor::write_pid_task_all_stats(int prev, int curr, uint64_t itv) {
        program_state_vector.clear();
        monitor_report.sync_work_type();

        for (auto &process : all_process_map) {
          st_pid all;
          for (int i = 0; i < 2; i++) {
            all.pstats[i] = std::make_unique<pid_stats>();
            memset(all.pstats[i].get(), 0, PID_STATS_SIZE);
          }

          Common::get_st_pid(&all, &process.second);

          for (auto &thread : process.second.sub_thread_map) {
            thread.second.name = process.second.name;

            Common::add(all.pstats[prev].get(), thread.second.pstats[prev].get());
            Common::add(all.pstats[curr].get(), thread.second.pstats[curr].get());
          }

          // Common::display_info(&all,prev,curr,itv);
          auto program = Common::get_program_state(&all, prev, curr, itv);
          auto iot_prev = !io_curr;
          auto pstc = all.pstats[io_curr].get();
          auto pstp = all.pstats[iot_prev].get();
          auto io_itv = Common::get_interval(io_uptime_cs[iot_prev], io_uptime_cs[io_curr]);

          program.rbytes = S_VALUE(pstp->read_bytes, pstc->read_bytes, io_itv) / 1024;
          program.wbytes = S_VALUE(pstp->write_bytes, pstc->write_bytes, io_itv) / 1024;
          program.cbytes = S_VALUE(pstp->cancelled_write_bytes, pstc->cancelled_write_bytes, io_itv) / 1024;

          // printf("bytes %lf %lf %lf\n", program.rbytes, program.wbytes, program.cbytes);

          program_state_vector.emplace_back(program);
          monitor_report.set_pid_stat(all, curr, uptime_cs[curr]);
        }

        monitor_report.update(uptime_cs[curr]);

        return 0;
      }

      int ProcessMonitor::write_stats_core(int prev, int curr) {
        uint64_t itv;
        int again = 0;

        itv = Common::get_interval(uptime_cs[prev], uptime_cs[curr]);

        again += write_pid_task_all_stats(prev, curr, itv);

        return again;
      }

      int ProcessMonitor::write_stats(int curr) {
        return write_stats_core(!curr, curr);
      }

      void ProcessMonitor::loop(bool print_info) {
        Common::read_uptime(&(uptime_cs[curr]));

        if (io_read_cnt_ % 5 == 0) {
          io_curr ^= 1;
          Common::read_uptime(&(io_uptime_cs[io_curr]));
        }

        read_stats(curr);
        write_stats(curr);
        curr ^= 1;
        io_read_cnt_++;
      }

      std::vector<ProgramState> ProcessMonitor::get_program_state_vector(void) {
        std::sort(program_state_vector.begin(), program_state_vector.end(),
                  [](ProgramState &a,
                     ProgramState &b) -> bool {
                    return a.name < b.name;
                  });
        return program_state_vector;
      }  // NOLINT
    }  // NOLINT
  }  // NOLINT
}  // NOLINT
